//
//  CalculatorBrain.swift
//  Stack
//
//  Created by YC on 2018/7/26.
//  Copyright © 2018年 YC. All rights reserved.
//

import UIKit

import Foundation

class CalculatorBrain {
    private enum Op:CustomStringConvertible {
        case Operand(Double)
        case UnaryOperation(String,(Double)->Double)
        case BinaryOperation(String,(Double,Double)->Double)

        var description:String{
            switch self {
            case .Operand(let Operand):
                return "\(Operand)"
            case .UnaryOperation(let symbol, _):
                return "\(symbol)"
            case .BinaryOperation(let symbol, _):
                return "\(symbol)"
            }
        }
    }
    
    private var opStack=[Op]()
    private var knowsOps=[String : Op]()
    
    init() {
        func learnOp(op: Op) {
            knowsOps[op.description] = op
        }
//    ***重点***
//    ***闭包***
//        switch operation {
//            //1最复杂的形式
//        case "*":performOperation2({(Double op1,Double op2)->Double in return op1*op2})
//            //2
//        case "/":performOperation2({(Double op1,Double op2)->Double in op2/op1})
//            //3
//        case "+":performOperation2({(op1,op2)->Double in op1+op2})
//            //4
//        case "−":performOperation2({$1-$0})
//            //其他形式performoperation(){$1-$0}
//            case "√":performOperation1(){sqrt($0)}
//        default:break
//        }
        
        //将操作符传入
        knowsOps["*"]=Op.BinaryOperation("*", *)
        knowsOps["/"]=Op.BinaryOperation("/", {$1/$0})
        learnOp(op: Op.BinaryOperation("+", +));
        //knowsOps["+"]=Op.BinaryOperation("+", +)
        knowsOps["−"]=Op.BinaryOperation("−", {$1-$0})
        //将sqrt函数作为参传入
        knowsOps["√"]=Op.UnaryOperation("√", sqrt)
    }

    private func evalucate(ops:[Op])->(result:Double?,remainOps:[Op]){
        if !ops.isEmpty{
            var remainOps=ops
            let op=remainOps.removeLast()
            switch op{
            case .Operand(let operand):
                return (operand,remainOps)
            case .UnaryOperation(_, let operation):
                let OperandEvalucate = evalucate(ops: remainOps)
                if let operand=OperandEvalucate.result{
                    return (operation(operand),OperandEvalucate.remainOps)
                }
            case .BinaryOperation(_, let operation):
                let op1Evalucate = evalucate(ops: remainOps)
                if let operand1=op1Evalucate.result{
                    let op2Evalucate = evalucate(ops: op1Evalucate.remainOps)
                    if let operand2=op2Evalucate.result{
                        return(operation(operand1,operand2),op2Evalucate.remainOps)
                    }
                }
            }
        }
        return(nil,ops)
    }
    
    func evalucate()->Double?{
        let (result,remainder)=evalucate(ops: opStack)
        print("\(opStack) = \(result!) with \(remainder) left over")
        return result
    }

    func pushOperand(operand:Double)->Double?{
        opStack.append(Op.Operand(operand))
        return evalucate()
    }

    func performOperation(Operation:String)->Double?{
        if let operation = knowsOps[Operation]{
            opStack.append(operation)
        }
        return evalucate()
    }
}
